import StreamZip from 'node-stream-zip';
import { ClassInfo, SearchResult } from './types.js';
import * as path from 'path';
import * as fs from 'fs/promises';

interface CacheEntry {
  data: ClassInfo[];
  timestamp: number;
  size: number;
}

export class ClassFinder {
  private jarCache = new Map<string, CacheEntry>();
  private classBytesCache = new Map<string, Buffer>();
  private readonly MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100MB
  private readonly CACHE_TTL = 3600000; // 1 hour
  private currentCacheSize = 0;

  async findClassesInJar(jarPath: string): Promise<ClassInfo[]> {
    console.error(`[ClassFinder] 开始分析JAR文件: ${jarPath}`);
    
    // Check cache with TTL
    const cached = this.jarCache.get(jarPath);
    if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
      console.error(`[ClassFinder] 使用缓存数据，跳过重复分析`);
      return cached.data;
    }

    const classes: ClassInfo[] = [];
    
    try {
      console.error(`[ClassFinder] 正在打开ZIP文件...`);
      const zip = new StreamZip.async({ file: jarPath });
      const entries = await zip.entries();
      
      const totalEntries = Object.keys(entries).length;
      console.error(`[ClassFinder] JAR文件包含 ${totalEntries} 个条目`);
      
      let classCount = 0;
      let innerClassCount = 0;
      
      for (const [entryName, entry] of Object.entries(entries)) {
        if (entryName.endsWith('.class') && !entry.isDirectory) {
          const classPath = entryName.replace(/\\/g, '/');
          const className = path.basename(classPath, '.class');
          const packagePath = path.dirname(classPath);
          const packageName = packagePath === '.' ? '' : packagePath.replace(/\//g, '.');
          const fullName = packageName ? `${packageName}.${className}` : className;
          
          if (!className.includes('$') || className.endsWith('$1')) {
            classes.push({
              className,
              packageName,
              fullName,
              jarPath
            });
            classCount++;
            
            if (classCount <= 5) {
              console.error(`[ClassFinder] 发现类: ${fullName}`);
            } else if (classCount === 6) {
              console.error(`[ClassFinder] ... (继续分析更多类)`);
            }
          } else {
            innerClassCount++;
          }
        }
      }
      
      console.error(`[ClassFinder] 分析完成: ${classCount} 个主类, ${innerClassCount} 个内部类`);
      await zip.close();
      
      // Add to cache with size management
      this.addToCache(jarPath, classes);
      
      return classes;
    } catch (error) {
      console.error(`[ClassFinder] 分析JAR文件失败 ${jarPath}: ${error}`);
      return [];
    }
  }

  async findClassesInBuffer(jarBuffer: Buffer): Promise<ClassInfo[]> {
    const tempPath = `/tmp/temp-jar-${Date.now()}.jar`;
    console.error(`[ClassFinder] 处理内存中的JAR数据，临时文件: ${tempPath}`);
    console.error(`[ClassFinder] JAR数据大小: ${(jarBuffer.length / 1024 / 1024).toFixed(2)} MB`);
    
    try {
      console.error(`[ClassFinder] 将JAR数据写入临时文件...`);
      await fs.writeFile(tempPath, jarBuffer);
      
      console.error(`[ClassFinder] 开始分析临时JAR文件...`);
      const classes = await this.findClassesInJar(tempPath);
      
      console.error(`[ClassFinder] 清理临时文件...`);
      await fs.unlink(tempPath);
      
      console.error(`[ClassFinder] 内存JAR处理完成，找到 ${classes.length} 个类`);
      return classes;
    } catch (error) {
      console.error(`[ClassFinder] 处理内存JAR失败: ${error}`);
      try {
        await fs.unlink(tempPath);
      } catch {}
      return [];
    }
  }

  async searchClasses(query: string, classes: ClassInfo[]): Promise<SearchResult> {
    console.error(`[ClassFinder] 开始搜索类，查询条件: "${query}"`);
    console.error(`[ClassFinder] 在 ${classes.length} 个类中搜索...`);
    
    const lowerQuery = query.toLowerCase();
    const matchedClasses = classes.filter(cls => {
      const classNameMatch = cls.className.toLowerCase().includes(lowerQuery);
      const fullNameMatch = cls.fullName.toLowerCase().includes(lowerQuery);
      const packageMatch = cls.packageName.toLowerCase().includes(lowerQuery);
      
      return classNameMatch || fullNameMatch || packageMatch;
    });

    console.error(`[ClassFinder] 搜索完成，找到 ${matchedClasses.length} 个匹配的类`);
    
    if (matchedClasses.length > 0 && matchedClasses.length <= 10) {
      console.error(`[ClassFinder] 匹配的类:`);
      matchedClasses.forEach(cls => {
        console.error(`[ClassFinder]   - ${cls.fullName}`);
      });
    } else if (matchedClasses.length > 10) {
      console.error(`[ClassFinder] 前10个匹配的类:`);
      matchedClasses.slice(0, 10).forEach(cls => {
        console.error(`[ClassFinder]   - ${cls.fullName}`);
      });
      console.error(`[ClassFinder]   ... 还有 ${matchedClasses.length - 10} 个`);
    }

    return {
      classes: matchedClasses,
      totalFound: matchedClasses.length
    };
  }

  async getClassSource(classInfo: ClassInfo): Promise<Buffer | null> {
    console.error(`[ClassFinder] 开始提取类字节码: ${classInfo.fullName}`);
    
    if (!classInfo.jarPath) {
      console.error(`[ClassFinder] 错误: 类信息中没有JAR路径`);
      return null;
    }

    // Check bytes cache
    const cacheKey = `${classInfo.jarPath}:${classInfo.fullName}`;
    const cachedBytes = this.classBytesCache.get(cacheKey);
    if (cachedBytes) {
      console.error(`[ClassFinder] 使用缓存的类字节码`);
      return cachedBytes;
    }

    try {
      console.error(`[ClassFinder] 打开JAR文件: ${classInfo.jarPath}`);
      const zip = new StreamZip.async({ file: classInfo.jarPath });
      
      const classPath = classInfo.fullName.replace(/\./g, '/') + '.class';
      console.error(`[ClassFinder] 查找类文件路径: ${classPath}`);
      
      const data = await zip.entryData(classPath);
      console.error(`[ClassFinder] 成功提取类字节码，大小: ${data.length} 字节`);
      
      // Add to bytes cache
      this.addBytesToCache(cacheKey, data);
      
      await zip.close();
      return data;
    } catch (error) {
      console.error(`[ClassFinder] 提取类字节码失败 ${classInfo.fullName}: ${error}`);
      return null;
    }
  }

  private addToCache(jarPath: string, classes: ClassInfo[]): void {
    const size = JSON.stringify(classes).length;
    
    // Check if cache size would exceed limit
    if (this.currentCacheSize + size > this.MAX_CACHE_SIZE) {
      this.evictOldestEntries(size);
    }
    
    this.jarCache.set(jarPath, {
      data: classes,
      timestamp: Date.now(),
      size: size
    });
    this.currentCacheSize += size;
    
    console.error(`[ClassFinder] 已缓存 ${jarPath}，缓存大小: ${(this.currentCacheSize / 1024 / 1024).toFixed(2)} MB`);
  }

  private addBytesToCache(key: string, data: Buffer): void {
    const size = data.length;
    
    // Check if cache size would exceed limit
    if (this.currentCacheSize + size > this.MAX_CACHE_SIZE) {
      this.evictOldestEntries(size);
    }
    
    this.classBytesCache.set(key, data);
    this.currentCacheSize += size;
  }

  private evictOldestEntries(requiredSpace: number): void {
    console.error(`[ClassFinder] 清理缓存以释放 ${(requiredSpace / 1024).toFixed(2)} KB 空间`);
    
    // Sort entries by timestamp
    const sortedEntries = Array.from(this.jarCache.entries())
      .sort((a, b) => a[1].timestamp - b[1].timestamp);
    
    let freedSpace = 0;
    for (const [key, entry] of sortedEntries) {
      if (freedSpace >= requiredSpace) break;
      
      this.jarCache.delete(key);
      freedSpace += entry.size;
      this.currentCacheSize -= entry.size;
    }
    
    // Also clear some bytes cache if needed
    if (freedSpace < requiredSpace && this.classBytesCache.size > 0) {
      const bytesToClear = Math.min(10, this.classBytesCache.size);
      const keysToDelete = Array.from(this.classBytesCache.keys()).slice(0, bytesToClear);
      
      for (const key of keysToDelete) {
        const bytes = this.classBytesCache.get(key);
        if (bytes) {
          freedSpace += bytes.length;
          this.currentCacheSize -= bytes.length;
          this.classBytesCache.delete(key);
        }
      }
    }
  }

  clearCache(): void {
    this.jarCache.clear();
    this.classBytesCache.clear();
    this.currentCacheSize = 0;
    console.error(`[ClassFinder] 缓存已清空`);
  }
}